package carcassonne.server.websocket.handler;

import carcassonne.lobby.Room;
import carcassonne.lobby.RoomManager;
import carcassonne.game.player.PlayerManager;
import carcassonne.server.ioc.WebSocketMessageDispatcher;
import carcassonne.server.websocket.WebSocketMessageType;
import carcassonne.server.websocket.controller.GameController;
import carcassonne.server.websocket.controller.LobbyController;
import carcassonne.server.websocket.message.MessageOut;
import carcassonne.user.UserManager;
import carcassonne.utils.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import carcassonne.server.websocket.message.MessageIn;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

public class WebSocketRequestHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private static final Logger logger = Logger.getLogger(WebSocketRequestHandler.class);

    public final WebSocketMessageDispatcher MessageDispatcher;

    public WebSocketRequestHandler() {
        List<Object> controllers = new ArrayList<>();
        controllers.add(new GameController());
        controllers.add(new LobbyController());
        MessageDispatcher = new WebSocketMessageDispatcher(controllers);
//        MessageDispatcher = new WebSocketMessageDispatcher("carcassonne.server.websocket");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext context, TextWebSocketFrame frame) throws Exception {

        Channel channel = context.channel();

        String raw = frame.text();

        // 心跳
        if ("boom".equals(raw)) return;

        MessageIn message;
        try {
            message = JSON.parseObject(raw, MessageIn.class);
        } catch (JSONException e) {
            return;
        }
        if (message == null) {
            MessageOut mo = MessageOut.Error("parse json error: " + raw);
            channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(mo)));
            return;
        }

        switch (message.type) {
            case WebSocketMessageType.Service:
                MessageDispatcher.Dispatch(channel, message, raw);
                break;
            case WebSocketMessageType.Action:
                TransmitAction(channel, message, raw);
                break;
            case WebSocketMessageType.OK:
                ConfirmAction(channel, message, raw);
                break;
            case WebSocketMessageType.Inquiry:
                TransmitInquiry(channel, raw);
                break;
            case WebSocketMessageType.Info:
            case WebSocketMessageType.Chat:
                // 直接转发
                TransmitMessage(channel, message, raw);
                break;
            case WebSocketMessageType.Heart:
                // 心跳
                break;
        }
    }

    public void TransmitMessage(Channel channel, MessageIn message, String raw) {
        Room room = RoomManager.GetInstance().GetRoom(channel);
        if (room == null) {
            logger.error("transmit message failed with channel " + channel.id().asShortText());
            return;
        }
        room.NotifyExcept(channel.id(), raw);
    }

    public void TransmitInquiry(Channel channel, String raw) {
        Room room = RoomManager.GetInstance().GetRoom(channel);
        if (room == null) {
            logger.error("transmit inquiry( failed with channel " + channel.id().asShortText());
            return;
        }
        room.NotifyHost(raw);
    }

    public void ConfirmAction(Channel channel, MessageIn message, String raw) {
        Room room = RoomManager.GetInstance().GetRoom(channel);
        if (room == null) {
            logger.error("transmit action failed with channel " + channel.id().asShortText());
            return;
        }
        room.ConfirmAction(channel, message);
    }

    public void TransmitAction(Channel channel, MessageIn message, String raw) {
        Room room = RoomManager.GetInstance().GetRoom(channel);
        if (room == null) {
            logger.error("transmit action failed with channel " + channel.id().asShortText());
            return;
        }
        room.OnReceiveAction(channel, message, raw);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext context, Object event) throws Exception {
        super.userEventTriggered(context, event);

//        WebSocketServerHandshaker handshaker = (WebSocketServerHandshaker) channelHandlerContext.channel()
//        .attr(AttributeKey.valueOf(WebSocketServerHandshaker.class, "HANDSHAKER")).get();
//        System.out.println(handshaker.uri());

        if (event instanceof WebSocketServerProtocolHandler.HandshakeComplete handshakeComplete) {

            // 握手完成，保存用户的channel
            HttpHeaders headers = handshakeComplete.requestHeaders();

            // token是否已经存在
            String token = headers.get("token");
            String playerID = UserManager.GetInstance().GetUserID(token);
            if (StringUtils.IsNullOrEmpty(playerID)) {
                context.writeAndFlush("not a login user");
                context.close();
                return;
            }
            PlayerManager.GetInstance().AddConnection(playerID, context.channel());

            String roomID = headers.get("roomID");
            RoomManager roomManager = RoomManager.GetInstance();
            if (RoomManager.GetInstance().HasRoom(roomID)) {
                // 加入房间
                if (!roomManager.AddPlayer(roomID, playerID)) {
                    context.writeAndFlush("join room failed");
                    context.close();
                    return;
                }
            } else if (RoomManager.GetInstance().HasPreparedRoom(roomID)){
                // 创建房间并且把这个用户直接加入进去
                if (!RoomManager.GetInstance().CreateRoom(roomID, playerID, context.channel())) {
                    // 创建房间失败
                    context.writeAndFlush("create room failed");
                    context.close();
                    return;
                }
            } else {
                context.writeAndFlush("error room id");
                context.close();
                return;
            }
            logger.info("new client incoming... " + playerID);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext context) throws Exception {
        super.channelActive(context);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);

        Channel channel = ctx.channel();

        PlayerManager.GetInstance().CloseConnection(channel);

        Room room = RoomManager.GetInstance().GetRoom(channel);
        if (room == null) {
            logger.error("null room with channel: " + channel.id().asShortText());
            return;
        }
        room.RemovePlayer(channel.id());
    }
}
